package com.cloud.zuul.config.security;


import com.lwj.framework.entity.AuthEntity;
import com.lwj.framework.security.auth.JwtRedisAccessTokenConverter;
import com.lwj.framework.security.auth.JwtRedisTokenStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.AccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.Collection;

/**
 * 认证服务器配置
 *
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
	static final Logger logger = LoggerFactory.getLogger(AuthorizationServerConfig.class);

	@Value("${spring.application.name}")
	private String resourceId;

	@Value("${security.jwt.signing}")
	private String signing;

	@Autowired
	private SecurityProperties securityProperties;

	@Autowired
	private RedisConnectionFactory redisConnectionFactory;

	@Autowired
	private AuthenticationManager authenticationManager;

	@Autowired
	private PasswordEncoder passwordEncoder;

	@Autowired
	private ClientDetailsService clientDetailsService;

	/**
	 * ClientDetailsServiceConfigurer用来配置客户端详情服务（ClientDetailsService），客户端详情信息在这里进行初始化，你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息
	 * @param clients
	 * @throws Exception
	 */
	@Override
	public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
		InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
		Collection<AuthEntity> list = securityProperties.getAuthList();
		for (AuthEntity authEntity : list) {
			String clientId = authEntity.getClientId();
			String secret = passwordEncoder.encode(authEntity.getSecret());
			String[] grantTypes = authEntity.getGrantTypes();
			String[] authorities = authEntity.getAuthorities();
			String[] scopes = authEntity.getScopes();
			Integer accessTokenValiditySeconds = authEntity.getAccessTokenValiditySeconds();
			Integer refreshTokenValiditySeconds = authEntity.getRefreshTokenValiditySeconds();
			builder.withClient(clientId).secret(secret).authorizedGrantTypes(grantTypes).authorities(authorities)
					.scopes(scopes).accessTokenValiditySeconds(accessTokenValiditySeconds)
					.refreshTokenValiditySeconds(refreshTokenValiditySeconds)
					.additionalInformation(authEntity.getExtension());
		}

	}

	/**
	 * AuthorizationServerSecurityConfigurer用来配置令牌端点(Token Endpoint)的安全约束.
	 * @param oauthServer
	 * @throws Exception
	 */
	@Override
	public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
		oauthServer.realm(resourceId + "/client");
	}

	/**
	 * AuthorizationServerEndpointsConfigurer用来配置授权（authorization）以及令牌（token）的访问端点和令牌服务(token services)。
	 * @param endpoints
	 * @throws Exception
	 */
	@Override
	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
		endpoints.userApprovalHandler(this.userApprovalHandler());
		endpoints.authenticationManager(this.authenticationManager);
		endpoints.accessTokenConverter(this.accessTokenConverter());
		endpoints.tokenStore(this.tokenStore());
	}

	/** 自定义token ***/
	@Bean
	public JwtAccessTokenConverter accessTokenConverter() {
		JwtRedisAccessTokenConverter accessTokenConverter = new JwtRedisAccessTokenConverter(securityProperties);
		accessTokenConverter.setSigningKey(signing);
		return accessTokenConverter;
	}

	@Bean
	public JwtRedisTokenStore tokenStore() {
		return new JwtRedisTokenStore(securityProperties, accessTokenConverter(), redisConnectionFactory);
	}

	@Bean
	public UserApprovalHandler userApprovalHandler() {
		TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
		handler.setTokenStore(tokenStore());
		handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
		handler.setClientDetailsService(clientDetailsService);
		return handler;
	}

	@Bean
	public ApprovalStore approvalStore() throws Exception {
		TokenApprovalStore store = new TokenApprovalStore();
		store.setTokenStore(tokenStore());
		return store;
	}

}
